/**
* @file gui_pointer_frontend.c
* @brief mouse or tp frontend
* @details 
* @author huangbin
* @version V1.0.0
* @date 2025-06-12
* @copyright All documents are copyrighted by Shenzhen Xinlongwei Technology Co., LTD.
*            They may not be used for commercial purposes without permission
*/


/*============================ INCLUDES ======================================*/

#define __GUI_MESSAGE_INHERIT__
#define __GUI_POINTER_FRONTEND_IMPLEMENT__

#include "./gui_pointer_frontend.h"

#if defined(__clang__)
//#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wunknown-warning-option"
#   pragma clang diagnostic ignored "-Wreserved-identifier"
#   pragma clang diagnostic ignored "-Wsign-conversion"
#   pragma clang diagnostic ignored "-Wpadded"
#   pragma clang diagnostic ignored "-Wcast-qual"
#   pragma clang diagnostic ignored "-Wcast-align"
#   pragma clang diagnostic ignored "-Wmissing-field-initializers"
#   pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#   pragma clang diagnostic ignored "-Wmissing-prototypes"
#   pragma clang diagnostic ignored "-Wunused-variable"
#   pragma clang diagnostic ignored "-Wunused-parameter"
#   pragma clang diagnostic ignored "-Wgnu-statement-expression"
#   pragma clang diagnostic ignored "-Wtautological-pointer-compare"
#elif __IS_COMPILER_ARM_COMPILER_5__
#elif __IS_COMPILER_GCC__
#   pragma GCC diagnostic push
#   pragma GCC diagnostic ignored "-Wformat="
#   pragma GCC diagnostic ignored "-Wpedantic"
#endif

/*============================ MACROS ========================================*/
#undef this
#define this    (*ptThis)
#undef base
#define base    (*ptBase)
/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/

enum {
    STAGE_1_TIMESTAMP,
    STAGE_1_MOVE_TIMESTAMP,
    STAGE_2_TIMESTAMP,
    STAGE_3_TIMESTAMP,
};

/*============================ PROTOTYPES ====================================*/
static
arm_fsm_rt_t __msg_pointer_frontend_process(
                                        gui_msg_frontend_t *ptBase,
                                        gui_msg_frontend_process_mode_t tMode);

static
gui_msg_core_t *__gui_helper_msg_pointer_frontend_read_input_fifo(void *pObj);


/*============================ LOCAL VARIABLES ===============================*/
static
const gui_i_msg_frontend_t GUI_HELPER_MSG_POINTER_FRONTEND_CHAIN = {
    .fnRead = __gui_helper_msg_pointer_frontend_read_input_fifo,
};

/*============================ GLOBAL VARIABLES ==============================*/
/*============================ IMPLEMENTATION ================================*/

ARM_NONNULL(1)
arm_2d_err_t gui_helper_msg_pointer_frontend_init(  
                            gui_msg_pointer_frontend_t *ptThis,
                            gui_msg_pointer_frontend_cfg_t *ptCFG,
                            gui_msg_pointer_frontend_pipeline_t *ptPipelines,
                            uint_fast8_t chPipelineNo)
{
    assert(NULL != ptThis);
    assert(NULL != ptCFG);

    memset(ptThis, 0, sizeof(gui_msg_pointer_frontend_t));

    gui_helper_msg_pointer_frontend_settings_set(ptThis, ptCFG);

    if ((0 == chPipelineNo) || (NULL == ptPipelines)) {
        return ARM_2D_ERR_MISSING_PARAM;
    }
    this.Pointers.chNumber = chPipelineNo;
    this.Pointers.ptPipelines = ptPipelines;

    /* initialize pipeline(s) */
    for (int_fast8_t chIndex = 0; chIndex < chPipelineNo; chIndex++) {

        gui_msg_pointer_frontend_pipeline_t *ptItem = &this.Pointers.ptPipelines[chIndex];

        memset(ptItem, 0, sizeof(gui_msg_pointer_frontend_pipeline_t));

        ptItem->u4Index = chIndex;

        /* initialize all FIFO*/
        arm_foreach(gui_helper_msg_fifo_t, ptItem->tFIFO, ptFIFO) {
            gui_helper_msg_fifo_init(ptFIFO);
        }
    }

    arm_with(  gui_msg_frontend_t, 
                &this.use_as__gui_msg_frontend_t) {
        _->fnProcess = &__msg_pointer_frontend_process;
        _->ptChain = &GUI_HELPER_MSG_POINTER_FRONTEND_CHAIN;

        gui_helper_msg_fifo_init(&_->tOutputFIFO);
    }
    
    return ARM_2D_ERR_NONE;
}


ARM_NONNULL(1, 2)
gui_msg_pointer_frontend_cfg_t *gui_helper_msg_pointer_frontend_settings_get(
                                    gui_msg_pointer_frontend_t *ptThis,
                                    gui_msg_pointer_frontend_cfg_t *ptCFG)
{
    assert(NULL != ptThis);
    assert(NULL != ptCFG);

    *ptCFG = this.tCFG;

    return ptCFG;
}
    

ARM_NONNULL(1, 2)
void gui_helper_msg_pointer_frontend_settings_set(
                                    gui_msg_pointer_frontend_t *ptThis,
                                    gui_msg_pointer_frontend_cfg_t *ptCFG)
{
    assert(NULL != ptThis);
    assert(NULL != ptCFG);

    this.tCFG = *ptCFG;
    if (0 == this.tCFG.hwLongPressThreshold ) {
        this.tCFG.hwLongPressThreshold = 3000;
    }
    if (0 == this.tCFG.u10DoubleClickLimit) {
        this.tCFG.u10DoubleClickLimit = 333;
    }
    if (0 == this.tCFG.hwHoverThreshold) {
        this.tCFG.hwHoverThreshold = 1000;
    }
}

static
gui_msg_core_t *__gui_helper_msg_pointer_frontend_read_input_fifo(void *pObj)
{
    assert(NULL != pObj);
    gui_msg_frontend_t *ptThis = (gui_msg_frontend_t *)pObj;

    return gui_helper_msg_fifo_get(&this.tOutputFIFO);
}

/*----------------------------------------------------------------------------*
 * Pointer Message Processing : Pointer Pipeline Dispatcher                   *
 * -------------------------------------------------------------------------- *
 * Brief: Dispatch pointer events to corresponding pipeline                   *
 *----------------------------------------------------------------------------*/
ARM_NONNULL(1)
static
arm_fsm_rt_t __msg_pointer_frontend_dispatcher(gui_msg_pointer_frontend_t *ptThis)
{
    assert(NULL != ptThis);
    gui_msg_frontend_t *ptBase = (gui_msg_frontend_t *)ptThis;
    assert(NULL != base.Dependency.tIn.fnRead);
    gui_msg_core_t *ptMSG = NULL;
    gui_msg_pointer_evt_t *ptPointerMSG = NULL;
    gui_msg_pointer_frontend_pipeline_t *ptPipeline = NULL;

ARM_PT_BEGIN(this.chDispatcherPT)

    do {
        /* get an message object */
    ARM_PT_ENTRY()
        ptMSG = ARM_2D_INVOKE(base.Dependency.tIn.fnRead, 
                        ARM_2D_PARAM(base.Dependency.tIn.pTarget));
        
        if (NULL == ptMSG) {
            ARM_PT_GOTO_PREV_ENTRY(arm_fsm_rt_cpl);
        }

        /* filter for pointer message */
        if (__GUI_MSG_POINTER_EVT != (ptMSG->u8ID & __GUI_MSG_POINTER_EVT_MSK)) {

            /* send to the output fifo directly */
            gui_helper_msg_fifo_append(&base.tOutputFIFO, ptMSG);
            continue;
        }

        ptPointerMSG = (gui_msg_pointer_evt_t *) ptMSG;

        if (ptPointerMSG->hwIndex >= this.Pointers.chNumber) {
            /* The index number exceed the number of channels supported.
             * Let's send the message to output fifo directly
             */
            gui_helper_msg_fifo_append(&base.tOutputFIFO, ptMSG);
            continue;
        }

        /* get the pipeline */
        ptPipeline = &this.Pointers.ptPipelines[ptPointerMSG->hwIndex];

        /* dispatch the msg to the target pipeline */
        gui_helper_msg_fifo_append(&ptPipeline->tFIFO[0], ptMSG);

    } while(1);

ARM_PT_END()

    return arm_fsm_rt_cpl;
}


/*----------------------------------------------------------------------------*
 * Pointer Message Processing : Pointer Pipeline stage 1                      *
 * -------------------------------------------------------------------------- *
 * Brief: Validate pointer down and up events and generate pointer hold event *
 *----------------------------------------------------------------------------*/
static
arm_fsm_rt_t __msg_pointer_frontend_pipeline_stage1(
                            gui_msg_pointer_frontend_t *ptThis,
                            gui_msg_pointer_frontend_pipeline_t *ptPipeline)
{
    assert(NULL != ptThis);
    assert(NULL != ptPipeline);
    gui_msg_frontend_t *ptBase = (gui_msg_frontend_t *)ptThis;

    gui_msg_core_t *ptMSG = NULL;
    gui_msg_pointer_evt_t *ptPointerMSG = NULL;

ARM_PT_BEGIN(ptPipeline->chPT[0])

    ptPipeline->bReceivedPointerMove = false;
    ptPipeline->lTimestamp[STAGE_1_TIMESTAMP] = 0;

    /* wait for the 1st pointer down */
    do {
    ARM_PT_ENTRY()

        int64_t lCurrent = arm_2d_helper_get_system_timestamp();

        ptMSG = gui_helper_msg_fifo_get(&ptPipeline->tFIFO[0]);
        if (NULL == ptMSG) {

            /* check hover */
            if (ptPipeline->bReceivedPointerMove) {
                
                int64_t lElapsedMS = arm_2d_helper_convert_ticks_to_ms(
                                                    lCurrent  
                                                -   ptPipeline->lTimestamp[STAGE_1_TIMESTAMP]);

                if (lElapsedMS >= this.tCFG.hwHoverThreshold) {
                    /* exceed the hover threshold */

                    /* generate a hover event  */
                    gui_msg_pointer_evt_t *ptMSGHover = 
                        (gui_msg_pointer_evt_t *)gui_helper_msg_pool_new(base.ptPool);
                    
                    if (NULL != ptMSGHover) {
                        
                        arm_with(gui_msg_pointer_evt_t, ptMSGHover) {
                            _->hwIndex = ptPipeline->u4Index;
                            _->hwPeriodInMs = MIN(lElapsedMS, __UINT16_MAX__);
                            _->use_as__arm_2d_location_t = ptPipeline->Stage1.tLastPoint;
                            _->use_as__gui_msg_core_t.ptTargetWidget = ptPipeline->Stage1.ptTargetWidgets;
                            _->use_as__gui_msg_core_t.u8ID = GUI_MSG_POINTER_EVT_HOVER;

                            /* send to next stage */
                            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[1], (gui_msg_core_t *)ptMSGHover);
                        }

                        /* clear the flag */
                        ptPipeline->bReceivedPointerMove = false;
                    }
                }
            }

            if (ptPipeline->bReceivedPointerMove) {
                ARM_PT_GOTO_PREV_ENTRY(arm_fsm_rt_wait_for_obj);
            } else {
                ARM_PT_GOTO_PREV_ENTRY(arm_fsm_rt_cpl);
            }
        }
        
        ptPointerMSG = (gui_msg_pointer_evt_t *)ptMSG;

        if (ptMSG->u8ID == GUI_MSG_POINTER_EVT_DOWN) {
            ptPointerMSG->hwPeriodInMs = 0;
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[1], ptMSG);

            /* start counting */
            ptPipeline->lTimestamp[STAGE_1_TIMESTAMP] = lCurrent;
            ptPipeline->lTimestamp[STAGE_1_MOVE_TIMESTAMP] = lCurrent;

            break;
        } else if ( (ptMSG->u8ID == GUI_MSG_POINTER_EVT_MOVE)
                ||  (ptMSG->u8ID == GUI_MSG_POINTER_EVT_UP)) {
            
            ptMSG->u8ID = GUI_MSG_POINTER_EVT_MOVE;

            if (0 == ptPipeline->lTimestamp[STAGE_1_TIMESTAMP]) {
                ptPointerMSG->hwPeriodInMs = 0;
            } else {
                ptPointerMSG->hwPeriodInMs = arm_2d_helper_convert_ticks_to_ms(
                                                    lCurrent  
                                                -   ptPipeline->lTimestamp[0]);
            }

            ptPipeline->lTimestamp[STAGE_1_TIMESTAMP] = lCurrent;

            ptPipeline->bReceivedPointerMove = true;
            ptPipeline->Stage1.ptTargetWidgets = ptMSG->ptTargetWidget;
            ptPipeline->Stage1.tLastPoint = ptPointerMSG->use_as__arm_2d_location_t;

            /* send to next stage */
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[1], ptMSG);
        } else {
            if (this.tCFG.bRemoveUnexpectedMessage) {
                /* delete the message */
                gui_helper_msg_pool_free(base.ptPool, (gui_msg_t *)ptMSG);
            } else {
                /* send to next stage */
                gui_helper_msg_fifo_append(&ptPipeline->tFIFO[1], ptMSG);
            }
        }
    } while(1);

    /* now we detected pointer down */
    do {
        ARM_PT_WAIT_OBJ_UNTIL((NULL != ptMSG),
            ptMSG = gui_helper_msg_fifo_get(&ptPipeline->tFIFO[0]);
        );
        int64_t lCurrent = arm_2d_helper_get_system_timestamp();
        int64_t lElapsedMS = arm_2d_helper_convert_ticks_to_ms(
                                            lCurrent  
                                        -   ptPipeline->lTimestamp[STAGE_1_TIMESTAMP]);

        ptPointerMSG = (gui_msg_pointer_evt_t *)ptMSG;

        if (ptMSG->u8ID == GUI_MSG_POINTER_EVT_UP) {
            /* update the period */
            ptPointerMSG->hwPeriodInMs = lElapsedMS;
            /* send to next stage */
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[1], ptMSG);
            ARM_PT_RETURN(arm_fsm_rt_cpl);
        } else if ( (ptMSG->u8ID == GUI_MSG_POINTER_EVT_DOWN)
                ||  (ptMSG->u8ID == GUI_MSG_POINTER_EVT_MOVE)) {
            ptPointerMSG->hwPeriodInMs = arm_2d_helper_convert_ticks_to_ms(
                                            lCurrent
                                        -   ptPipeline->lTimestamp[STAGE_1_MOVE_TIMESTAMP]);
            ptPipeline->lTimestamp[STAGE_1_MOVE_TIMESTAMP] = lCurrent;

            /* change message ID */
            ptMSG->u8ID = GUI_MSG_POINTER_EVT_HOLD;
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[1], ptMSG);
        } else {
            if (this.tCFG.bRemoveUnexpectedMessage) {
                /* delete the message */
                gui_helper_msg_pool_free(base.ptPool, (gui_msg_t *)ptMSG);
            } else {
                /* send to next stage */
                gui_helper_msg_fifo_append(&ptPipeline->tFIFO[1], ptMSG);
            }
        }
    } while(1);

ARM_PT_END()

    return arm_fsm_rt_cpl;
}

/*----------------------------------------------------------------------------*
 * Pointer Message Processing : Pointer Pipeline stage 2                      *
 * -------------------------------------------------------------------------- *
 * Brief: generate pointer click and pointer long pressing events             *
 *----------------------------------------------------------------------------*/
static
arm_fsm_rt_t __msg_pointer_frontend_pipeline_stage2(
                            gui_msg_pointer_frontend_t *ptThis,
                            gui_msg_pointer_frontend_pipeline_t *ptPipeline)
{
    assert(NULL != ptThis);
    assert(NULL != ptPipeline);
    gui_msg_frontend_t *ptBase = (gui_msg_frontend_t *)ptThis;

    gui_msg_core_t *ptMSG = NULL;
    gui_msg_pointer_evt_t *ptPointerMSG = NULL;

ARM_PT_BEGIN(ptPipeline->chPT[1])

    /* wait for pointer down */
    do {
    ARM_PT_ENTRY()
        ptMSG = gui_helper_msg_fifo_get(&ptPipeline->tFIFO[1]);
        if (NULL == ptMSG) {
            ARM_PT_GOTO_PREV_ENTRY(arm_fsm_rt_cpl);
        }

        ptPointerMSG = (gui_msg_pointer_evt_t *)ptMSG;

        if (ptMSG->u8ID == GUI_MSG_POINTER_EVT_DOWN) {
            /* start counting */
            ptPipeline->lTimestamp[STAGE_2_TIMESTAMP] = arm_2d_helper_get_system_timestamp();

            ptPipeline->Stage2.ptTargetWidgets = ptMSG->ptTargetWidget;
            ptPipeline->Stage2.tLastPoint = ptPointerMSG->use_as__arm_2d_location_t;

            /* append message to the next stage */
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[2], ptMSG);
            break;
        } else {
            /* append other message to the next stage directly */
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[2], ptMSG);
        }

    } while(1);

    /* try to generate click or long pressed event */
    do {
        ARM_PT_WAIT_OBJ_UNTIL((NULL != ptMSG),

            int64_t lCurrent = arm_2d_helper_get_system_timestamp();
            int64_t lElapsedMs = arm_2d_helper_convert_ticks_to_ms( 
                                    lCurrent 
                                -   ptPipeline->lTimestamp[STAGE_2_TIMESTAMP] );
            
            /* check long pressing event */
            if (lElapsedMs >= this.tCFG.hwLongPressThreshold) {
                /* generate a long press event  */
            
                gui_msg_pointer_evt_t *ptMSGLongPress = 
                    (gui_msg_pointer_evt_t *)
                        gui_helper_msg_pool_new(base.ptPool);
                
                if (NULL != ptMSGLongPress) {
                    arm_with(gui_msg_pointer_evt_t, ptMSGLongPress) {

                        _->hwPeriodInMs = MIN(lElapsedMs, __UINT16_MAX__);
                        _->hwIndex = ptPipeline->u4Index;
                        _->use_as__arm_2d_location_t 
                            = ptPipeline->Stage2.tLastPoint;
                        _->use_as__gui_msg_core_t.ptTargetWidget 
                            = ptPipeline->Stage2.ptTargetWidgets;
                        _->use_as__gui_msg_core_t.u8ID 
                            = GUI_MSG_POINTER_EVT_LONG_PRESSING;

                    }

                    /* append message to the next stage */
                    gui_helper_msg_fifo_append( &ptPipeline->tFIFO[2], 
                                                (gui_msg_core_t *)ptMSGLongPress);
                    break;

                }
            }
        
            ptMSG = gui_helper_msg_fifo_get(&ptPipeline->tFIFO[1]);
        );

        if (ptMSG->u8ID == GUI_MSG_POINTER_EVT_UP) {
            /* detect a click event */

            /* append message to the next stage */
            gui_helper_msg_fifo_append( &ptPipeline->tFIFO[2], 
                                        (gui_msg_core_t *)ptMSG);

            ARM_PT_WAIT_RESOURCE_UNTIL((ptMSGClick != NULL),
                gui_msg_pointer_evt_t *ptMSGClick = 
                    (gui_msg_pointer_evt_t *)
                        gui_helper_msg_pool_new(base.ptPool);
            );

            arm_with(gui_msg_pointer_evt_t, ptMSGClick) {

                _->hwPeriodInMs = MIN(lElapsedMs, __UINT16_MAX__);
                _->hwIndex = ptPipeline->u4Index;
                _->use_as__arm_2d_location_t 
                    = ptPipeline->Stage2.tLastPoint;
                _->use_as__gui_msg_core_t.ptTargetWidget 
                    = ptPipeline->Stage2.ptTargetWidgets;
                _->use_as__gui_msg_core_t.u8ID 
                    = GUI_MSG_POINTER_EVT_CLICK;
            }

            /* append message to the next stage */
            gui_helper_msg_fifo_append( &ptPipeline->tFIFO[2], 
                                        (gui_msg_core_t *)ptMSGClick);
            
            ARM_PT_RETURN(arm_fsm_rt_cpl);
        } else {
            /* append other message to the next stage directly */
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[2], ptMSG);
        }

        

    } while(1);

    /* wait for the final pointer up event */
    do {
        ARM_PT_WAIT_OBJ_UNTIL((NULL != ptMSG),
            ptMSG = gui_helper_msg_fifo_get(&ptPipeline->tFIFO[1]);
        );

        if (ptMSG->u8ID == GUI_MSG_POINTER_EVT_UP) {
            /* detect a click event */

            /* append message to the next stage */
            gui_helper_msg_fifo_append( &ptPipeline->tFIFO[2], 
                                        (gui_msg_core_t *)ptMSG);
            ARM_PT_RETURN(arm_fsm_rt_cpl);
        } else {
            /* append other message to the next stage directly */
            gui_helper_msg_fifo_append(&ptPipeline->tFIFO[2], ptMSG);
        }
    } while(1);

ARM_PT_END()

    return arm_fsm_rt_cpl;
}

/*----------------------------------------------------------------------------*
 * Pointer Message Processing : Pointer Pipeline stage 3                      *
 * -------------------------------------------------------------------------- *
 * Brief: generate pointer click and pointer long pressing events             *
 *----------------------------------------------------------------------------*/
static
arm_fsm_rt_t __msg_pointer_frontend_pipeline_stage3(
                            gui_msg_pointer_frontend_t *ptThis,
                            gui_msg_pointer_frontend_pipeline_t *ptPipeline)
{
    assert(NULL != ptThis);
    assert(NULL != ptPipeline);
    gui_msg_frontend_t *ptBase = (gui_msg_frontend_t *)ptThis;

    gui_msg_core_t *ptMSG = NULL;
    gui_msg_pointer_evt_t *ptPointerMSG = NULL;

ARM_PT_BEGIN(ptPipeline->chPT[2])

    /* wait for the first click event */
    do {
    ARM_PT_ENTRY()
        ptMSG = gui_helper_msg_fifo_get(&ptPipeline->tFIFO[2]);
        if (NULL == ptMSG) {
            ARM_PT_GOTO_PREV_ENTRY(arm_fsm_rt_cpl);
        }

        if (ptMSG->u8ID == GUI_MSG_POINTER_EVT_CLICK) {
            /* get the first click event */

            /* start counting */
            ptPipeline->lTimestamp[STAGE_3_TIMESTAMP] 
                = arm_2d_helper_get_system_timestamp();

            /* append the message to the click FIFO */
            gui_helper_msg_fifo_append(&(ptPipeline->Stage3.tClickFIFO), ptMSG);
            break;
        } else {
            /* append other messages to the output FIFO */
            gui_helper_msg_fifo_append(&base.tOutputFIFO, ptMSG);
        }
    } while(1);


    /* wait for the 2nd click event */
    do {
    ARM_PT_WAIT_OBJ_UNTIL((NULL != ptMSG),

            int64_t lElapsedMs = arm_2d_helper_convert_ticks_to_ms(
                                        arm_2d_helper_get_system_timestamp()
                                    -   ptPipeline->lTimestamp[STAGE_3_TIMESTAMP]
                                );
            if (lElapsedMs > this.tCFG.u10DoubleClickLimit) {
                ptMSG = gui_helper_msg_fifo_get(&(ptPipeline->Stage3.tClickFIFO));
                assert(NULL != ptMSG);

                gui_helper_msg_fifo_append(&base.tOutputFIFO, ptMSG);
                break;
            }
        
            ptMSG = gui_helper_msg_fifo_get(&ptPipeline->tFIFO[2]);
        );

        if (ptMSG->u8ID == GUI_MSG_POINTER_EVT_CLICK) {
            
            /* free the previous click message */
            gui_helper_msg_pool_free(
                base.ptPool, 
                (gui_msg_t *)gui_helper_msg_fifo_get(&(ptPipeline->Stage3.tClickFIFO)));
            
            ptPointerMSG = (gui_msg_pointer_evt_t *)ptMSG;

            /* change message ID */
            ptMSG->u8ID = GUI_MSG_POINTER_EVT_DOUBLE_CLICK;

            /* update double click internal */
            ptPointerMSG->hwPeriodInMs = lElapsedMs;

            /* send the message to the output FIFO */
            gui_helper_msg_fifo_append(&base.tOutputFIFO, ptMSG);
            break;
        } else {
            /* append other messages to the output FIFO */
            gui_helper_msg_fifo_append(&base.tOutputFIFO, ptMSG);
        }

    } while(1);

ARM_PT_END()

    return arm_fsm_rt_cpl;
}

ARM_NONNULL(1,2)
static
arm_fsm_rt_t __msg_pointer_frontend_pipeline_task(
                            gui_msg_pointer_frontend_t *ptThis,
                            gui_msg_pointer_frontend_pipeline_t *ptPipeline)
{
    assert(NULL != ptThis);
    assert(NULL != ptPipeline);

    arm_fsm_rt_t tResult = arm_fsm_rt_cpl;


    do {
        bool bWaitForResources = false;
        /* stage 1 */
        do {
            arm_fsm_rt_t tReturn = __msg_pointer_frontend_pipeline_stage1(
                                                                    ptThis, 
                                                                    ptPipeline);
            switch (tReturn) {
                case arm_fsm_rt_cpl:
                    break;
                case arm_fsm_rt_wait_for_obj:
                    if (tResult == arm_fsm_rt_cpl) {
                        tResult = arm_fsm_rt_wait_for_obj;
                    }
                    break;
                case arm_fsm_rt_wait_for_res:
                    bWaitForResources = true;
                default:
                    tResult = tReturn;
                    break;
            }
        } while(0);

        /* stage 2 */
        do {
            arm_fsm_rt_t tReturn = __msg_pointer_frontend_pipeline_stage2(
                                                                    ptThis, 
                                                                    ptPipeline);
            switch (tReturn) {
                case arm_fsm_rt_cpl:
                    break;
                case arm_fsm_rt_wait_for_obj:
                    if (tResult == arm_fsm_rt_cpl) {
                        tResult = arm_fsm_rt_wait_for_obj;
                    }
                    break;
                case arm_fsm_rt_wait_for_res:
                    bWaitForResources = true;
                default:
                    tResult = tReturn;
                    break;
            }
        } while(0);

        /* stage 3 */
        do {
            arm_fsm_rt_t tReturn = __msg_pointer_frontend_pipeline_stage3(
                                                                    ptThis, 
                                                                    ptPipeline);
            switch (tReturn) {
                case arm_fsm_rt_cpl:
                    break;
                case arm_fsm_rt_wait_for_obj:
                    if (tResult == arm_fsm_rt_cpl) {
                        tResult = arm_fsm_rt_wait_for_obj;
                    }
                    break;
                case arm_fsm_rt_wait_for_res:
                    bWaitForResources = true;
                default:
                    tResult = tReturn;
                    break;
            }
        } while(0);

        if (tResult < 0) {
            /* error detected */
            break;
        } else if ( (tResult == arm_fsm_rt_cpl) 
                 || (tResult == arm_fsm_rt_wait_for_obj)) {
            /* complete condition */
            break;
        } else if (bWaitForResources) {
            tResult = arm_fsm_rt_wait_for_res;
            break;
        }

    } while(0);

    return tResult;
}


static
arm_fsm_rt_t __msg_pointer_frontend_process(
                                        gui_msg_frontend_t *ptBase,
                                        gui_msg_frontend_process_mode_t tMode)
{
    gui_msg_pointer_frontend_t *ptThis = (gui_msg_pointer_frontend_t *)ptBase;

    assert(NULL != base.Dependency.tIn.fnRead);
    arm_fsm_rt_t tResult = arm_fsm_rt_cpl;

    /* run until we reach the complete condition */
    do {
        tResult = arm_fsm_rt_cpl;
        bool bWaitForResources = false;

        /* the critical stage: message dispatcher */
        if (tMode == GUI_FRONTEND_PROCESS_MODE_ALL || tMode == GUI_FRONTEND_PROCESS_MODE_CRITICAL) {
            arm_fsm_rt_t tReturn = __msg_pointer_frontend_dispatcher(ptThis);

            switch (tReturn) {
                case arm_fsm_rt_cpl:
                    break;
                case arm_fsm_rt_wait_for_obj:
                    if (tResult == arm_fsm_rt_cpl) {
                        tResult = arm_fsm_rt_wait_for_obj;
                    }
                    break;
                case arm_fsm_rt_wait_for_res:
                    bWaitForResources = true;
                default:
                    tResult = tReturn;
                    break;
            }
        }

        if (tMode == GUI_FRONTEND_PROCESS_MODE_ALL || tMode == GUI_FRONTEND_PROCESS_MODE_NON_CRITICAL) {
            
            /* call each pipeline */
            arm_foreach(gui_msg_pointer_frontend_pipeline_t,
                        this.Pointers.ptPipelines,
                        this.Pointers.chNumber,
                        ptPipeline) {
                
                arm_fsm_rt_t tReturn = __msg_pointer_frontend_pipeline_task(ptThis, ptPipeline);

                switch (tReturn) {
                    case arm_fsm_rt_cpl:
                        break;
                    case arm_fsm_rt_wait_for_obj:
                        if (tResult == arm_fsm_rt_cpl) {
                            tResult = arm_fsm_rt_wait_for_obj;
                        }
                        break;
                    case arm_fsm_rt_wait_for_res:
                        bWaitForResources = true;
                    default:
                        tResult = tReturn;
                        break;
                }
            }
        }

        if (tResult < 0) {
            /* error detected */
            break;
        } else if ( (tResult == arm_fsm_rt_cpl) 
                 || (tResult == arm_fsm_rt_wait_for_obj)) {
            /* complete condition */
            break;
        } else if (bWaitForResources) {
            tResult = arm_fsm_rt_wait_for_res;
            break;
        }
    //} while(0);
    } while(gui_helper_msg_fifo_is_empty(&this.use_as__gui_msg_frontend_t.tOutputFIFO));

    return tResult;
}